#include"client.h"

int main(int argc,char *argv[]){
    //./client 192.168.4.128 1234
    ARGC_CHECK(argc,3);
    int sockFd = socket(AF_INET,SOCK_STREAM,0);
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(atoi(argv[2]));
    addr.sin_addr.s_addr = inet_addr(argv[1]);
    int ret =connect(sockFd,(struct sockaddr*)&addr,sizeof(addr));
    ERROR_CHECK(ret,-1,"connect");

    //登录模块
    bool across = false;
    do{
        char buf[1024]={0};
        printf("请输入服务器密码:\n");
        int i;
        scanf("%s",buf);
        if(across == false){
            send(sockFd,buf,sizeof(buf),0);
            bzero(buf,sizeof(buf));
            recv(sockFd,buf,sizeof(buf),0);
            if(strcmp("true",buf)==0){
                across = true;
                puts("true");
            }
            else{
                puts("密码错误");
            }
        }
    }while(across == false); //登录判断

    //功能
    while(1){
        //int command = startClient();
        int command = 1;
        printf("\n可使用的指令编号有:\n");
        printf("1. ls 列出相应目录文件\n");
        printf("2. cd 进入对应目录\n");
        printf("3. pwd 显示目前所在路径\n");
        printf("4. puts 将本地文件上传至服务器\n");
        printf("5. gets 下载服务器文件到本地\n");
        printf("6. remove 删除服务器上文件\n");
        printf("7. 查看日志\n");
        printf("请输入一个指令编号\n");
        ret = scanf("%d",&command);
        while(ret == 0){
            printf("输入错误,请重新输入！\n"); 
            ret = scanf("%d",&command); 
            getchar();
        }

        char command_buf[1024]={0};
        sprintf(command_buf,"%d",command);
        ret = send(sockFd,command_buf,sizeof(command_buf),MSG_NOSIGNAL);
        if(ret == -1){
            puts("服务器端关闭");
            break;
        }

        char buf[1024]={0};
        switch (command)
        {
            case 1:
                func1(sockFd);//ls 列出相应目录文件
                break;

            case 2:
                func2(sockFd);// cd 进入对应目录
                break;

            case 3:
                func3(sockFd);//pwd 显示目前所在路径
                break;

            case 4:
                func4(sockFd);//puts 将本地文件上传至服务器
                break;

            case 5:{
                func5(sockFd);//gets 文件名 下载服务器文件到本地
                break;
            }

            case 6:{
                func6(sockFd);//remove 删除服务器上文件
                break;
            }

            case 7:
                func7(sockFd);//查看日志
                break;
        
            default:
                break;
        }
        getchar();
        printf("请输入任意指令以继续!\n");
        getchar();
    }

    printf("感谢使用！\n");
    close(sockFd);
}

int func1(int netFd){
    printf("ll结果:\n");
    char buf[4096]={0};
    bzero(buf,sizeof(buf));
    //puts(buf);
    recv(netFd,buf,sizeof(buf),0);
    puts(buf);
}

int func2(int netFd){
    printf("请输入cd指令:\n");
    char buf[1024]={0};
    getchar();
    //puts(buf);
    fgets(buf,sizeof(buf),stdin);
    puts(buf);
    send(netFd,buf,sizeof(buf),0);
    bzero(buf,sizeof(buf));
    printf("cd后目录为:\n");
    recv(netFd,buf,sizeof(buf),0);
    puts(buf);
}

int func3(int netFd){
    printf("\n当前目录为:");
    char buf[1024]={0};
    bzero(buf,sizeof(buf));
    puts(buf);
    recv(netFd,buf,sizeof(buf),0);
    puts(buf);
}

int func4(int netFd){
    printf("请输入要上传的文件名：\n");
    char send_file[1024]={0};
    scanf("%s",send_file);
    sendFile(send_file,netFd);
    puts("上传完成");
}

int func5(int netFd){
    printf("请输入要下载的文件名:\n");
    char buf[1024]={0};
    scanf("%s",buf);
    send(netFd,buf,sizeof(buf),0);
    recvFile(netFd);
}

int func6(int netFd){
    printf("请输入要删除的文件名:\n");
    char buf[1024]={0};
    scanf("%s",buf);
    send(netFd,buf,sizeof(buf),0);
    bzero(buf,sizeof(buf));
    recv(netFd,buf,sizeof(buf),0);
    puts(buf);
}

int func7(int netFd){
    char buf[40960] = {0};
    recv(netFd,buf,sizeof(buf),0);
    puts(buf);
}

int sendFile(char filename[],int sdf)
{
   train_t t;
   bzero(&t,sizeof(t));
   t.dataLength=strlen(filename);
   strcpy(t.buf,filename);
   //printf("文件名字%s\n",t.buf);
   int file=send(sdf,&t,sizeof(int)+t.dataLength,MSG_NOSIGNAL);//发送下载文件名字长度+文件名字
   ERROR_CHECK(file,-1,"发送服务端被下载文件名字长度+文件名字");
   int fd=open(t.buf,O_RDWR);
   //FILE *fp=fopen(t.buf,"r+");
   bzero(&t,sizeof(t));
   struct stat st;
   int retstat=fstat(fd,&st);
   ERROR_CHECK(retstat,-1,"fstat");
   long localsize=st.st_size; //服务端被下载文件大小
   printf("服务端被下载文件大小%ldM\n",localsize/M);
   t.dataLength=sizeof(st.st_size);
   memcpy(t.buf,&st.st_size,sizeof(st.st_size));
   int ret6=send(sdf,&t,t.dataLength+sizeof(t.dataLength),MSG_NOSIGNAL);//发送客户端文件的大小长度+文件大小
   ERROR_CHECK(ret6,-1,"发送服务端被下载文件的大小长度与大小");
   long filesize;
   int dataLength;
   int ret1=recv(sdf,&dataLength,sizeof(int),MSG_WAITALL); //接收客户端同名文件大小长度
   int ret2=recv(sdf,&filesize,dataLength,MSG_WAITALL); //接收客户端同名文件大小
   ERROR_CHECK(ret1,-1,"接收服客户端同名文件大小长度");
   char* p=(char*)mmap(NULL,st.st_size,PROT_READ,MAP_SHARED,fd,0);
   if(p!=NULL)
   {
     printf("mmap成功\n");
   }
   off_t index=0;
   if(filesize!=0)
   {
      printf("断点重传\n");
      printf("客户端同名文件大小%ldM\n",filesize/M);
      index=filesize;
   }
   int i=0;
   while(index<localsize)
   {
     if(localsize-index >= sizeof(t.buf))
     {
       t.dataLength=sizeof(t.buf);
     }
     else
     {
       t.dataLength=localsize-index;
     }
       memcpy(t.buf,p+index,t.dataLength);
       index=index+t.dataLength;
       int ret7=send(sdf,&t,sizeof(int)+t.dataLength,MSG_NOSIGNAL);
       //printf("%5.2lf%% %ld\r",100.0*index/localsize,index);
       //fflush(stdout);
    }
    t.dataLength=0;
    send(sdf,&t,sizeof(int),MSG_NOSIGNAL);
    int ret8=munmap(p,st.st_size);
    ERROR_CHECK(ret8,-1,"解除mmap映射");
    printf("发送文件大小: %ldM\n",index/M);
    close(fd);
    return 0;
   }
int recvFile(int netFd){
    train_t t;
    bzero(&t,sizeof(t));
    //先接收文件名长度
    int ret = recv(netFd,&t.dataLength,sizeof(t.dataLength),MSG_WAITALL);
    ERROR_CHECK(ret,-1,"recv nameLenght");
    //再接收文件名字
    ret = recv(netFd,t.buf,t.dataLength,MSG_WAITALL);
    ERROR_CHECK(ret,-1,"recv fileName");
    long fileSize;
    //获取描述文件总大小的火车头
    recv(netFd,&t.dataLength,sizeof(t.dataLength),MSG_WAITALL);
    ERROR_CHECK(ret,-1,"recv dataLength");
    //再获取文件总大小
    recv(netFd,&fileSize,t.dataLength,MSG_WAITALL);
    ERROR_CHECK(ret,-1,"recv fileSize");
    printf("目标文件大小: %ldM\n",fileSize/M);
    //打开或者创建临时文件
    int fd = open(t.buf,O_RDWR|O_CREAT,0666);
    ERROR_CHECK(fd,-1,"open");
    //获取临时文件总大小，并发送给发送端
    struct stat statbuf;
    ret = fstat(fd,&statbuf);
    ERROR_CHECK(ret,-1,"fstat");
    bzero(&t,sizeof(t));
    t.dataLength = sizeof(statbuf.st_size);
    //把文件大小放到小火车车厢中
    memcpy(t.buf,&statbuf.st_size,sizeof(statbuf.st_size));
    //再发送文件大小
    send(netFd,&t,sizeof(t.dataLength)+t.dataLength,MSG_NOSIGNAL);
    off_t downSize = statbuf.st_size;//当前已经下载文件的大小
    printf("当前文件大小:%ldM\n",downSize/M);
    off_t needSize = (fileSize - downSize)/M;//需下载的量
    printf("需下载:%ldM\n",needSize);
    off_t lastSize = statbuf.st_size;//在上一次完成下载文件总大小千分之一时的文件大小
    off_t slice = fileSize/1000;//总文件大小千分之一
    //设置当前文件偏移量，让其在之前数据之后读写
    if(statbuf.st_size != 0){
        lseek(fd,downSize,SEEK_SET);
    }
    //char bar[50] = {0};
    //int i = 0;
    //int count = 0;
    //char *lab="-\\|/";
    //当文件小于于100M
        while(1){
             bzero(&t,sizeof(t));
             ret = recv(netFd,&t.dataLength,sizeof(int),MSG_WAITALL);
             ERROR_CHECK(ret,-1,"recv dataLength");
             if(t.dataLength == 0){
                 break;
             }
             downSize += t.dataLength;
             if(downSize - lastSize > slice){
                 double precent = (double)downSize/fileSize;
                 printf("[%5.2lf%%]\r",100.0*precent);
                 fflush(stdout);
                // count++;
                // if(count >= 20){
                //     bar[i++] = '#';
                //     bar[i] = '\0';
                // }
                // lastSize = downSize;
             }
             ret = recv(netFd,t.buf,t.dataLength,MSG_WAITALL);
             ERROR_CHECK(ret,-1,"recv t.buf");
             write(fd,t.buf,t.dataLength);
        }
    printf("[100.00%s]\n","%");
    close(fd);
}